package edu.bu.cs673.superkids123.common.model;

import java.util.Date;

import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;
import javax.persistence.Table;

import org.hibernate.annotations.NotFound;
import org.hibernate.annotations.NotFoundAction;

@Entity
@Table(name = "Achievement_Rules")
public class AchievementRule {

	@Id
	@GeneratedValue
	@Column(name = "ruleID")
	private int id;
	
	@Column(name = "ruleMetric")
	private Metric metric;
	
	@Column(name = "ruleActivity")
	private Activity activity;

	@Column(name = "dateValue")
	private Date dateValue;
	
	@Column(name = "numberValue")
	private int numberValue;
	
	@Column(name = "valueType")
	private ValueType type;
	
	@Column(name = "ruleCompare")
	private Compare comparer;
	
	@ManyToOne
	@JoinColumn(name="AchievementsID")
	@NotFound(action=NotFoundAction.IGNORE)
	private Achievement achievement = null;

	/**
	 * Determines if this rule has been fulfilled.
	 * 
	 * @return false if not complete or is invalid in some way.
	 */
	public boolean isValid(Object value, Metric source) {
		
		boolean result = false;
		
		switch (source) {
		case Count:
		case Score:
			result = isValid(value, ValueType.Integer);
			break;
		case Time:
			result = isValid(value, ValueType.Time);
			break;
		default:
			break;
		}
		
		return result;
	}

	/**
	 * Determines if this rule has been fulfilled.
	 * 
	 * @return false if not complete or is invalid in some way.
	 */
	public boolean isValid(Object value, ValueType valueType) {

		boolean result = false;

		// TODO: Value validation

		switch (valueType) {
		case Integer:
			int intA = (Integer) value;
			int intB = numberValue;
			result = compareInt(intA, intB, comparer);
			break;
		case FloatingPoint:
			break;
		case Time:
			Date timeA = (Date) value;
			Date timeB = dateValue;
			result = compareTime(timeA, timeB, comparer);
			break;
		default:
			break;
		}

		return result;
	}

	private boolean compareInt(int a, int b, Compare compare) {
		boolean result = false;

		switch (compare) {
		case GreaterThan:
			result = (a > b);
			break;
		case LessThan:
			result = (a < b);
			break;
		case GreaterThanOrEqual:
			result = (a >= b);
			break;
		case LessThanOrEqual:
			result = (a <= b);
			break;
		case Equals:
			result = (a == b);
			break;
		case NotEquals:
			result = (a != b);
		default:
			result = false;
			break;
		}

		return result;
	}

	private boolean compareTime(Date a, Date b, Compare compare) {
		boolean result = false;

		switch (compare) {
		case GreaterThan:
			result = (a.getTime() > b.getTime());
			break;
		case LessThan:
			result = (a.getTime() < b.getTime());
			break;
		case GreaterThanOrEqual:
			result = (a.getTime() >= b.getTime());
			break;
		case LessThanOrEqual:
			result = (a.getTime() <= b.getTime());
			break;
		case Equals:
			result = (a.getTime() == b.getTime());
			break;
		case NotEquals:
			result = (a.getTime() != b.getTime());
		default:
			result = false;
			break;
		}

		return result;
	}

	// Enums

	public enum Metric {
		Count, Score, Time
	}

	public enum Activity {
		All, AllGames, AllLessons, AnyGame, AnyLesson, LessonCount, LessonAdd, GameMatch, GameMath,
		GameArchery, GameClick, GameSubtraction
	}
	
	public enum Compare {
		GreaterThan, LessThan, GreaterThanOrEqual, LessThanOrEqual, Equals, NotEquals;
	}

	public enum ValueType {
		Integer, FloatingPoint, Time;
	}

	// Setters & Getters

	public int getId() {
		return id;
	}

	public void setId(int id) {
		this.id = id;
	}

	public Metric getMetric() {
		return metric;
	}

	public void setMetric(Metric source) {
		this.metric = source;
	}

	public Activity getActivity() {
		return activity;
	}

	public void setActivity(Activity activity) {
		this.activity = activity;
	}

	public Compare getComparer() {
		return comparer;
	}

	public void setComparer(Compare comparer) {
		this.comparer = comparer;
	}

	public Achievement getAchievement() {
		return achievement;
	}

	public void setAchievement(Achievement achievement) {
		this.achievement = achievement;
	}
	
	public Object getValue() {
		if (metric == Metric.Time) {
			return dateValue;
		}
		
		return numberValue;
	}
	
	public void setValue(Object obj) {
		
		if (obj instanceof Date) {
			numberValue = 0;
			dateValue = (Date) obj;
		} else if (obj instanceof Integer) {
			dateValue = null;
			numberValue = (Integer) obj;
		}
	}
}
